#pragma once

#include <iostream>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
#include <cstdlib>
#include <sstream>
#include <unordered_map>
#include <algorithm>
// stat查看文件是否存在
#include <sys/stat.h>
#include "Util.hpp"
#include "Log.hpp"

#define SEP ": "
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define PAGE_404 "/404.html"
#define HTTP_VERSION "HTTP/1.0"
#define END_LINE "\r\n"

static std::string Code2Desc(int code)
{
    switch (code)
    {
    case 200:
        return "OK";
    case 404:
        return "NOT_FOUND";
    default:
        return "UNKNOWN";
    }
}

static std::string Suffix2Desc(const std::string &suffix)
{
    static std::unordered_map<std::string, std::string> suffix2desc = {
        {".html", "text/html"},
        {".css", "text/css"},
        {".js", "application/javascript"},
        {".jpg", "application/x-jpg"},
        {".xml", "application/xml"}};
    auto iter = suffix2desc.find(suffix);
    if (iter != suffix2desc.end())
    {
        // 找到映射
        return iter->second;
    }
    // 找不到默认html
    return "text/html";
}

class HttpRequest
{
public:
    std::string request_line;                // 请求行
    std::vector<std::string> request_header; // 请求报头
    std::string blank;
    std::string request_body; // 请求报文
    std::string suffix;       // 文件后缀

    // 解析之后的请求行信息
    std::string method;
    std::string uri;
    std::string version;

    // url信息
    std::string path;
    std::string query_string;

    // 报头信息
    std::unordered_map<std::string, std::string> header_kv;

    // 报文信息
    int ContentLength;

    bool cgi;

public:
    HttpRequest()
        : ContentLength(0), cgi(false)
    {
    }

    void Clear()
    {
        request_line.clear();
        request_header.clear();
        request_body.clear();
        suffix.clear();

        method.clear();
        uri.clear();
        version.clear();

        path.clear();
        query_string.clear();
        header_kv.clear();
        ContentLength = 0;
        cgi = false;
    }

    ~HttpRequest()
    {
    }
};

#define OK 200
#define NOT_FOUND 404
#define BAD_REQUEST 400
#define SERVER_ERROR 500

class HttpResponce
{
public:
    std::string status_line;                  // 响应行
    std::vector<std::string> responce_header; // 响应报头
    std::string suffix;                       // 后缀
    std::string blank;
    std::string responce_body;
    int body_size; // 正文大小

    int status_code; // 状态码
    int fd;          // 打开访问文件地文件描述符

public:
    HttpResponce()
        : status_code(OK), blank(END_LINE), fd(-1)
    {
    }

    void Clear()
    {
        status_line.clear();
        responce_header.clear();
        suffix.clear();
        responce_body.clear();
        body_size = 0;
        status_code = OK;
        fd = -1;
    }

    ~HttpResponce()
    {
    }
};

// 读取请求，分析请求，构建响应
// IO通信
class EndPoint
{
private:
    int sock;
    int code;
    HttpRequest http_request;
    HttpResponce http_responce;

    bool stop;

private:
    bool RecvHttpRequestLine()
    {
        ssize_t s = Util::ReadLine(sock, http_request.request_line);
        if (s > 0)
        {
            http_request.request_line.resize(http_request.request_line.size() - 1);
            LOG(INFO, http_request.request_line);
        }
        else
        {
            stop = true;
        }
        return stop;
    }

    int ProcessCgi()
    {
        // 目标文件一定是存在的,且是可执行的
        auto &method = http_request.method;
        auto &query_string = http_request.query_string; // GET
        auto &body_text = http_request.request_body;    // POST
        auto &bin = http_request.path;
        int content_length = http_request.ContentLength; // 报文长度

        // 环境变量必须放在外面，否则可能会被释放，导致getenv找不到环境变量
        std::string method_env;
        std::string query_string_env;
        std::string content_length_env;
        // 在父进程角度
        int input[2];
        int output[2];

        // 创建管道
        if (pipe(input) < 0)
        {
            LOG(ERROR, "pipe input error!");
            return SERVER_ERROR;
        }
        if (pipe(output) < 0)
        {
            LOG(ERROR, "pipe output error!");
            return SERVER_ERROR;
        }
        // 创建子进程，进行进程替换
        pid_t pid = fork();
        if (pid == 0)
        {
            // 子进程
            // 进程替换，执行目标程序
            // 父进程需要把数据传给子进程,进程间通信->匿名管道通信
            // 交换input,output,使input，output是站在子进程角度,input[0]读取，output[1]输出
            std::swap(input[0], output[0]);
            std::swap(input[1], output[1]);

            close(input[1]);
            close(output[0]);

            method_env = "METHOD=";
            method_env += method;
            putenv(const_cast<char *>(method_env.c_str()));
            std::cout << "METHOD:" << method << std::endl;
            if (method == "GET")
            {
                query_string_env = "QUERY_STRING=";
                query_string_env += query_string;
                // 必须将query_string_env 放到域外面,否则出域之后自动析构会导致getenv找不到环境变量
                putenv(const_cast<char *>(query_string_env.c_str()));
                LOG(WARNING, "ADD QUERY_STRING ENV"); // LOG使用cout打印，所以在dup之后无法输出LOG信息
                LOG(INFO, getenv("QUERY_STRING"));
            }
            else if (method == "POST")
            {
                content_length_env = "CONTENT_LENGTH=";
                content_length_env += std::to_string(content_length);
                // 必须将content_length_env 放到域外面,否则出域之后自动析构会导致getenv找不到环境变量
                int ret = putenv(const_cast<char *>(content_length_env.c_str()));
                std::cout << "putenv_ret:" << ret << std::endl;
            }
            else
            {
                // do nonthing
            }
            LOG(WARNING, "POST DEBUG!!");
            LOG(WARNING, bin);
            // 程序替换，只替换代码和数据，并不替换内核进程相关的数据结构，包括:文件描述符表
            // 约定: 让被替换后的进程，读取管道等价于读取标准输入，写入管道等价于标准输出
            // 实现方法: 重定向,在程序替换前进行重定向,万事俱备之后再dup
            dup2(input[0], 0);
            dup2(output[1], 1);
            // 替换成功后，目标子进程通过文件描述符 0和1 和父进程进行通信
            execl(bin.c_str(), bin.c_str(), nullptr);
        }
        else if (pid > 0)
        {
            // 父进程
            // 关闭管道读写端,0是读,1是写
            close(input[1]);
            close(output[0]);
            if (method == "POST")
            {
                int allsize = body_text.size();
                int total = 0;
                int size = 0;
                while (true)
                {
                    size = write(output[1], body_text.c_str(), allsize - total);
                    if (size > 0 && total < content_length)
                    {
                        total += size;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            // 从子进程获取数据
            char ch;
            std::string res_body;
            while (read(input[0], &ch, 1) > 0)
            {
                res_body += ch;
            }
            http_responce.responce_body = res_body;
            http_responce.body_size = res_body.size();
            int status = 0;
            pid_t ret = waitpid(pid, &status, 0); // 进程阻塞等待，不影响其他任务处理
            if (ret == pid)
            {
                if (WIFEXITED(status))
                {
                    if (WEXITSTATUS(status) == 0)
                    {
                        code = OK;
                    }
                    else
                    {
                        code = SERVER_ERROR;
                    }
                }
                else
                {
                    code = SERVER_ERROR;
                }
            }
            close(input[0]);
            close(output[1]);
        }
        else
        {
            // 创建失败
            LOG(ERROR, "fork error!");
            code = SERVER_ERROR;
        }
        return code;
    }

    int ProcessNonCgi(int size)
    {
        http_responce.fd = open(http_request.path.c_str(), O_RDONLY);
        if (http_responce.fd >= 0)
        {
            return OK;
        }
        return NOT_FOUND;
    }

    bool RecvHttpRequestHeader()
    {
        std::string line;
        while (line != "\n")
        {
            line.clear(); // 清空line
            if (Util::ReadLine(sock, line) <= 0)
            {
                stop = true;
                break;
            } // 把报头每一行读取到line
            if (line == "\n")
                break;
            // 去掉末尾\n
            // line不能为空行,如为空则跳出循环
            if (line.size())
                line.resize(line.size() - 1);
            else
                break;
            // LOG(INFO, line);
            http_request.request_header.push_back(line);
        }
        if (line == "\n")
            http_request.blank = line;
        return stop;
    }

    void ParseHttpRequestLine()
    {
        // 解析请求行
        HttpRequest &request = http_request;
        std::stringstream ss(request.request_line);
        ss >> request.method >> request.uri >> request.version;
        // 将请求方法转为全大写
        auto &method = request.method;
        std::transform(method.begin(), method.end(), method.begin(), ::toupper);
    }

    // 解析请求报头
    void ParseHttpRequestHeader()
    {
        std::string key;
        std::string value;
        for (auto &str : http_request.request_header)
        {
            Util::CutString(str, key, value, SEP);
            http_request.header_kv[key] = value;
        }
    }

    bool IsNeedRecvHttpRequestBody()
    {
        // 通过判断method 判断是否含有正文
        auto method = http_request.method;
        if (method == "POST")
        {
            auto &header_kv = http_request.header_kv;
            auto it = header_kv.find("Content-Length");
            if (it != header_kv.end())
            {
                // 把正文字节数保存到ContentLength
                http_request.ContentLength = atoi(it->second.c_str());
                return true;
            }
            else
            {
                http_request.ContentLength = 0;
                return false;
            }
        }
        else
        {
            http_request.ContentLength = 0;
            return false;
        }
    }

    // 获取请求正文
    bool RecvHttpRequestBody()
    {
        if (IsNeedRecvHttpRequestBody())
        {
            // 获取正文部分
            int bodylength = http_request.ContentLength;
            auto &body = http_request.request_body;
            char ch = 0;
            while (bodylength)
            {
                ssize_t s = recv(sock, &ch, 1, 0);
                if (s > 0)
                {
                    --bodylength;
                    body.push_back(ch);
                }
                else
                {
                    stop = true;
                    break;
                }
            }
            LOG(INFO, body);
        }
        return stop;
    }

    void HandlerError(std::string page)
    {
        // 返回404页面
        // 把cgi设为false,返回错误页面
        http_request.cgi = false;
        int fd = open(page.c_str(), O_RDONLY);
        if (fd > 0)
        {
            LOG(INFO, "open success!");
            http_responce.fd = fd;
            struct stat st;
            stat(page.c_str(), &st);
            http_responce.body_size = st.st_size;
            std::string line = "Content-Type: text/html";
            line += END_LINE;
            http_responce.responce_header.push_back(line);
            line = "Content-Length: ";
            line += std::to_string(st.st_size);
            line += END_LINE;
            http_responce.responce_header.push_back(line);
        }
        else
        {
            LOG(ERROR, "open error!");
        }
    }

    void BuildOkResponce()
    {
        // 添加Content-Length,Content-Type
        std::string line = "Content-Type: ";
        line += Suffix2Desc(http_request.suffix);
        line += END_LINE;
        http_responce.responce_header.push_back(line);

        line = "Content-Length: ";
        line += std::to_string(http_responce.body_size);
        line += END_LINE;
        http_responce.responce_header.push_back(line);

        http_responce.status_code = OK;
        http_responce.status_line = HTTP_VERSION;
        http_responce.status_line += " ";
        http_responce.status_line += std::to_string(http_responce.status_code);
        http_responce.status_line += " ";
        http_responce.status_line += Code2Desc(http_responce.status_code);
        http_responce.status_line += END_LINE;
    }

    // 构建错误响应
    void BuildHttpResponceHelper()
    {
        auto &code = http_responce.status_code;
        // 构建状态行
        auto &status_line = http_responce.status_line;
        status_line += HTTP_VERSION;
        status_line += " ";
        status_line += std::to_string(code);
        status_line += " ";
        status_line += Code2Desc(code);
        status_line += END_LINE;
        // 构建响应正文
        std::string PAGE = WEB_ROOT;
        switch (code)
        {
        case OK:
            BuildOkResponce();
            break;
        case NOT_FOUND:
            PAGE += PAGE_404;
            HandlerError(PAGE);
            break;
        case BAD_REQUEST:
            PAGE += PAGE_404;
            HandlerError(PAGE);
            break;
        case SERVER_ERROR:
            PAGE += PAGE_404;
            HandlerError(PAGE);
            break;

        default:
            break;
        }
    }

    void BuildHttpResponce()
    {
        struct stat st;
        int size = 0;
        ssize_t found = 0;
        std::string _path;
        int stat_ret;
        if (http_request.method != "GET" && http_request.method != "POST")
        {
            // 非法请求
            LOG(WARNING, "Method is not right,method :" + http_request.method);
            code = BAD_REQUEST;
            http_responce.status_code = BAD_REQUEST;
            goto END;
        }
        if (http_request.method == "GET")
        {
            // GET方法,解析url
            size_t pos = http_request.uri.find('?');
            if (pos != std::string::npos)
            {
                // 找到‘？’,有参数
                Util::CutString(http_request.uri, http_request.path, http_request.query_string, "?");
                // 有参数，需要处理，cgi = true
                http_request.cgi = true;
            }
            else
            {
                http_request.path = http_request.uri;
            }
        }
        else if (http_request.method == "POST")
        {
            // POST
            http_request.cgi = true;
            http_request.path = http_request.uri;
        }
        else
        {
            // do nonthing
        }
        // 1.保证path是否合法？
        // path不能从服务器根目录开始，应该是从web根目录开始
        _path = http_request.path;
        http_request.path = WEB_ROOT;
        http_request.path += _path;
        // std::cout << "DEBUG:" << http_request.path << std::endl;
        // 如果path末尾是 '/'那是访问整个根目录或者目录，请求非法，改成访问首页
        if (http_request.path[http_request.path.size() - 1] == '/')
        {
            http_request.path += HOME_PAGE;
        }
        // std::cout << "DEBUG:" << http_request.path << std::endl;
        // 判断path下的资源是否存在
        stat_ret = stat(http_request.path.c_str(), &st); // 存在返回0，不存在返回-1
        if (stat_ret == 0)
        {
            // 说明资源存在
            LOG(INFO, "资源存在");
            if (S_ISDIR(st.st_mode))
            {
                // 每个目录下都要有一个index.html
                // 说明是一个目录，这是不被允许的
                http_request.path += "/";
                http_request.path += HOME_PAGE;
                // 重新获取一下文件信息
                stat(http_request.path.c_str(), &st);
            }
            // 说明文件存在，不是目录
            // 文件可能是一个可执行程序，需要特殊处理
            if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
            {
                // 说明是可执行文件，准备处理
                http_request.cgi = true;
            }
            // 获取文件大小
            size = st.st_size;
            http_responce.body_size = size;
        }
        else
        {
            // 说明资源不存在
            std::string info = http_request.path;
            info += " Not Found!";
            LOG(WARNING, info);
            code = NOT_FOUND;
            http_responce.status_code = NOT_FOUND;
            goto END;
        }
        // 到了这里说明请求没有问题了
        // 文件后缀
        found = http_request.path.rfind(".");
        if (found == std::string::npos)
        {
            // 没有找到后缀
            http_request.suffix = ".html"; // 默认html
        }
        else
        {
            // 找到后缀
            http_request.suffix = http_request.path.substr(found);
        }
        if (http_request.cgi)
        {
            // 需要cgi处理
            LOG(INFO, "CGI处理");
            code = ProcessCgi();
            http_responce.status_code = code;
        }
        else
        {
            // 不需要CGI处理
            // 返回要构建HTTP响应
            LOG(INFO, "非CGI处理");
            code = ProcessNonCgi(size); // 简单网页返回，返回静态网页
            http_responce.status_code = code;
        }
    END:
        BuildHttpResponceHelper();
        return;
    }

    // 发送响应
    void SendHttpResponce()
    {
        // 发送响应状态行
        auto &line = http_responce.status_line;
        send(sock, line.c_str(), line.size(), 0);
        // 发送响应报头
        for (auto &iter : http_responce.responce_header)
        {
            send(sock, iter.c_str(), iter.size(), 0);
        }
        // 发送响应空行
        send(sock, http_responce.blank.c_str(), http_responce.blank.size(), 0);
        // 发送响应正文
        if (http_request.cgi)
        {
            auto &responce_body = http_responce.responce_body;
            size_t size = 0;
            size_t total = 0;
            const char *start = responce_body.c_str();
            while ((size = send(sock, start + total, http_responce.body_size - total, 0)) > 0)
            {
                total += size;
            }
        }
        else
        {

            int ret = sendfile(sock, http_responce.fd, nullptr, http_responce.body_size);
            if (ret == -1)
            {
                perror("sendfile");
                LOG(FATAL,"sendfile error");
            }
            // 关闭fd
            close(http_responce.fd);
        }
    }

public:
    EndPoint(int _sock = 0)
        : sock(_sock), stop(false)
    {
    }

    void ChangeSock(int _sock)
    {
        sock = _sock;
    }

    void Clear()
    {
        sock = -1;
        code = 200;
        http_request.Clear();
        http_responce.Clear();
        stop = false;
    }

    bool Stop()
    {
        return stop;
    }

    bool RcvRequest()
    {
        // 1.获取请求行
        bool ret1 = RecvHttpRequestLine();
        // 2.获取报头
        bool ret2 = RecvHttpRequestHeader();
        return ((!ret1) && (!ret2));
    }

    void ParseRequest()
    {
        // 1.解析请求行
        ParseHttpRequestLine();
        // 2.解析报头
        ParseHttpRequestHeader();
        // 3.获取正文
        RecvHttpRequestBody();
    }

    void BuildResponce()
    {
        BuildHttpResponce();
    }

    void SendResponce()
    {
        SendHttpResponce();
    }
    ~EndPoint()
    {
    }
};

// 使用thread_local修饰ep 每一个线程都拥有一个ep 每次使用完Clear之后可以复用
static thread_local EndPoint ep;

// 回调函数
class CallBack
{
public:

    CallBack()
    {}

    void operator()(int sock)
    {
        HandlerRequest(sock);
    } 

    void HandlerRequest(int sock)
    {

        // EndPoint *ep = new EndPoint(sock);
        // 接受请求
        ep.ChangeSock(sock);
        if (ep.RcvRequest())
        {
            // 分析请求
            ep.ParseRequest();
        }
        if (!ep.Stop())
        {
            // LOG(INFO,"Recv No Error,Begin Build Responce And Send.....");
            // 构建响应
            ep.BuildResponce();
            // 发送响应
            ep.SendResponce();
        }
        else
        {
            LOG(WARNING,"Recv ERROR! Stop And End!");
        }
        ep.Clear();
        close(sock);
        LOG(INFO, "Handler Request End");
    }

    ~CallBack()
    {}
};
